package dnl.util.ui.table;

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.MenuElement;
import javax.swing.SwingUtilities;


/**
 * @author Daniel Or
 * @version 1.0
 */
public class CustomizedTable extends JTable {

	Action editAction = new AbstractAction("Edit") {

		@Override
		public void actionPerformed(ActionEvent ae) {
			int selectedRow = CustomizedTable.this.getSelectedRow();
			openEditor(selectedRow);
		}
	};
	JMenuItem editMI = new JMenuItem(editAction);
	JPopupMenu popup = new JPopupMenu();
	ExternalRowEditor rowEditor;
	boolean showSelectionBorder;
	boolean showSelectedRowWhenNotFocused = true;
	int selectedRow = -1;

	/**
	 * Constructor for CustomizedTable.
	 */
	public CustomizedTable() {
		super();
		init();
	}

	/**
	 * Constructor for CustomizedTable.
	 * 
	 * @param dm
	 */
	public CustomizedTable(TModel dm) {
		super(dm);
		init();
	}

	/**
	 * 
	 * @param obj if <code>obj</code> is null, removes any selection.
	 * @param column
	 */
	public void setSelectedObject(Object obj, int column){
		if(obj == null){
			CustomizedTable.this.getSelectionModel().clearSelection();
			return;
		}
		TModel tmodel = getTModel();
		int count = tmodel.getRowCount();
		for (int i = 0; i < count; i++) {
			Object o = tmodel.getValueAt(i, column);
			if(obj.equals(o)){
				this.setRowSelectionInterval(i, i);
				//setSelectedRow(i);
				this.scrollCurrentRowToVisible();
				return;
			}
		}
		if(CustomizedTable.this.getRowCount() > 0){
			CustomizedTable.this.setRowSelectionInterval(0, 0);
		}
	}
	
	public void setSelectedRow(int row){
		this.selectedRow = row;
	}
	
	public void unfocusHidesSelectedLine(boolean unfocusHides) {
		showSelectedRowWhenNotFocused = unfocusHides;
	}

	/**
	 * Performs general initializations.
	 *
	 */
	public void init() {
		addToPopup(editMI);
		this.addFocusListener(new FocusListener() {

			@Override
			public void focusGained(FocusEvent e) {
				if (showSelectedRowWhenNotFocused && selectedRow > 0
						&& selectedRow < CustomizedTable.this.getRowCount()) {
					CustomizedTable.this.setRowSelectionInterval(selectedRow, selectedRow);
				} else if (CustomizedTable.this.getRowCount() > 0) {
					CustomizedTable.this.setRowSelectionInterval(0, 0);
				}
			}

			@Override
			public void focusLost(FocusEvent e) {
				if (showSelectedRowWhenNotFocused) {
					setSelectedRow(CustomizedTable.this.getSelectedRow());
					CustomizedTable.this.clearSelection();
				}
			}
		});
		this.addMouseListener(new MouseAdapter() {

			@Override
			public void mouseClicked(MouseEvent e) {
				CustomizedTable.this.requestFocus();
				if (popup == null)
					return;
				final int row = rowAtPoint(e.getPoint());
				if (row < 0)
					return;
				if (SwingUtilities.isRightMouseButton(e)) {
					editMI.setEnabled(isRowEditable(row));
					CustomizedTable.this.setRowSelectionInterval(row, row);
					preparePopup();
					popup.show(CustomizedTable.this, e.getX(), e.getY());
				}
				else if (rowEditor != null && e.getClickCount() == 2) {
					Runnable r = new Runnable() {
						@Override
						public void run() {
							openEditor(row);
						}
					};
					new Thread(r).start();
				}
			}
		});
	}

	/**
	 * 
	 */
	@Override
	public void requestFocus() {
		super.requestFocus();
		Runnable r = new Runnable() {
			@Override
			public void run() {
				scrollCurrentRowToVisible();		
			}
		};
		SwingUtilities.invokeLater(r);
		
	}
	
	/**
	 * 
	 *
	 */
	public void scrollCurrentRowToVisible() {
		int selectedRow = getSelectedRow();
		if(selectedRow >= 0){
			scrollToVisible(selectedRow, selectedRow);
		}
	}
	
	/**
	 * Assumes table is contained in a JScrollPane. Scrolls the
     * cell (rowIndex, vColIndex) so that it is visible within the viewport.
	 */
    public void scrollToVisible(int rowIndex, int vColIndex) {
        if (!(this.getParent() instanceof JViewport)) {
            return;
        }
        JViewport viewport = (JViewport)this.getParent();
    
        // This rectangle is relative to the table where the
        // northwest corner of cell (0,0) is always (0,0).
        Rectangle rect = this.getCellRect(rowIndex, vColIndex, true);
    
        // The location of the viewport relative to the table
        Point pt = viewport.getViewPosition();
    
        // Translate the cell location so that it is relative
        // to the view, assuming the northwest corner of the
        // view is (0,0)
        rect.setLocation(rect.x-pt.x, rect.y-pt.y);
    
        // Scroll the area into view
        viewport.scrollRectToVisible(rect);
    }
	
	/**
	 * 
	 * @param jpop
	 */
	public void setPopupMenu(JPopupMenu jpop) {
		this.popup = jpop;
	}

	/**
	 * 
	 * @param show
	 */
	public void setShowSelectionBorder(boolean show) {
		this.showSelectionBorder = show;
	}

	/**
	 * 
	 * @return
	 */
	public boolean isSelectionBorderShown() {
		return this.showSelectionBorder;
	}

	/**
	 * @see javax.swing.JComponent#getToolTipText(MouseEvent)
	 */
	@Override
	public String getToolTipText(MouseEvent e) {
		int row = rowAtPoint(e.getPoint());
		int column = columnAtPoint(e.getPoint());
		if (row == 0) {
			return null;
		}
		else {
			RowData rowData = getTModel().getRow(row);
			if (rowData == null)
				return null;
			return rowData.getTooltip(column);
		}
	}

	/**
	 * @return RowData
	 */
	public RowData getSelectedRowData() {
		int selectedRow = this.getSelectedRow();
		return getTModel().getRow(selectedRow);
	}

	public RowData getRowData(int index) {
		return getTModel().getRow(index);
	}

	/**
	 *  
	 */
	public void editSelectedRow() {
		if (rowEditor == null)
			return;
		int selectedRow = this.getSelectedRow();
		openEditor(selectedRow);
	}

	/**
	 * Adds the given component to the popup menu.
	 * 
	 * @param component
	 */
	public void addToPopup(JComponent component) {
		if (this.popup == null)
			return;
		popup.add(component);
	}

	/**
	 * @param rowEditor
	 */
	public void setExternalEditor(ExternalRowEditor rowEditor) {
		this.rowEditor = rowEditor;
	}

	public boolean isRowEditable(int row) {
		RowData rw = getTModel().getRow(row);
		if (rw == null)
			return false;
		return rw.isEditable();
	}

	protected TModel getTModel() {
		return (TModel) getModel();
	}

	private void preparePopup() {
		if (popup == null)
			return;
		MenuElement[] elements = popup.getSubElements();
		RowData rd = getSelectedRowData();
		if (rd == null)
			return;
		for (int i = 0; i < elements.length; i++) {
			if (elements[i] instanceof PopupMenuItem) {
				((PopupMenuItem) elements[i]).setEnabled(rd);
			}
		}
	}

	private void openEditor(int row) {
		if (row < 0)
			return;
		RowData rowData = getTModel().getRow(row);
		if (rowData == null)
			return;
		JDialog dlg = rowEditor.getEditingDialog(rowData);
		if (dlg != null)
			dlg.setVisible(true);
		else {
			String msg = rowEditor.getRefusalReason();
			JOptionPane.showMessageDialog(CustomizedTable.this, msg);
		}
	}
}